"""
Flask application factory for Ruiyun Cloud Documentation Platform.

This module initializes the Flask application with all extensions,
blueprints, and configuration.
"""
import os
import click
from flask import Flask
from flask_migrate import Migrate
from datetime import timedelta

# Import database instance
from app.models import db, Administrator

# Import configuration
from app.config import config

# Initialize Flask-Migrate
migrate = Migrate()


def create_app(config_name=None):
    """
    Application factory function.

    Args:
        config_name (str): Configuration name ('development', 'production', 'testing')
                          Defaults to FLASK_ENV environment variable or 'development'

    Returns:
        Flask: Configured Flask application instance
    """
    app = Flask(__name__)

    # Determine configuration
    if config_name is None:
        config_name = os.getenv('FLASK_ENV', 'development')

    # Load configuration
    app.config.from_object(config[config_name])

    # Initialize extensions
    db.init_app(app)
    migrate.init_app(app, db)

    # Support for reverse proxy (X-Forwarded-* headers)
    from werkzeug.middleware.proxy_fix import ProxyFix
    app.wsgi_app = ProxyFix(
        app.wsgi_app,
        x_for=1,      # Trust X-Forwarded-For
        x_proto=1,    # Trust X-Forwarded-Proto
        x_host=1,     # Trust X-Forwarded-Host
        x_prefix=1    # Trust X-Forwarded-Prefix
    )

    # Configure session management
    app.config['SESSION_COOKIE_HTTPONLY'] = True
    app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
    app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(minutes=30)

    # Register blueprints
    register_blueprints(app)

    # Register CLI commands
    register_commands(app)

    # Register error handlers
    register_error_handlers(app)

    # Add security headers
    @app.after_request
    def set_security_headers(response):
        """Add security headers to all responses."""
        response.headers['X-Content-Type-Options'] = 'nosniff'
        response.headers['X-Frame-Options'] = 'DENY'
        response.headers['X-XSS-Protection'] = '1; mode=block'

        # Add HSTS only in production with HTTPS
        if not app.debug and app.config.get('SESSION_COOKIE_SECURE'):
            response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'

        return response

    return app


def register_blueprints(app):
    """
    Register Flask blueprints for public and admin routes.

    Args:
        app (Flask): Flask application instance
    """
    # Import blueprints
    from app.blueprints.public import public_bp
    from app.blueprints.admin import admin_bp

    # Register blueprints
    app.register_blueprint(public_bp)
    app.register_blueprint(admin_bp, url_prefix='/admin')


def register_commands(app):
    """
    Register Flask CLI commands.

    Args:
        app (Flask): Flask application instance
    """

    @app.cli.command('create-admin')
    def create_admin():
        """
        Create an administrator account from environment variables.

        Reads ADMIN_USERNAME and ADMIN_PASSWORD from environment.
        If admin already exists, prints a message and exits.
        """
        username = os.getenv('ADMIN_USERNAME', 'admin')
        password = os.getenv('ADMIN_PASSWORD')

        if not password:
            click.echo('Error: ADMIN_PASSWORD environment variable not set')
            click.echo('Please set ADMIN_PASSWORD in your .env file or environment')
            return

        # Check if admin already exists
        existing_admin = Administrator.query.filter_by(username=username).first()
        if existing_admin:
            click.echo(f'Administrator "{username}" already exists')
            return

        # Create new admin
        admin = Administrator(username=username)
        try:
            admin.set_password(password)
            db.session.add(admin)
            db.session.commit()
            click.echo(f'Administrator "{username}" created successfully')
        except ValueError as e:
            click.echo(f'Error creating administrator: {e}')
        except Exception as e:
            db.session.rollback()
            click.echo(f'Error creating administrator: {e}')


def register_error_handlers(app):
    """
    Register custom error handlers.

    Args:
        app (Flask): Flask application instance
    """
    from flask import render_template

    @app.errorhandler(404)
    def not_found_error(error):
        """Handle 404 Not Found errors."""
        return render_template('404.html'), 404

    @app.errorhandler(413)
    def request_entity_too_large(error):
        """Handle 413 Request Entity Too Large errors (file upload too big)."""
        return render_template('413.html'), 413

    @app.errorhandler(500)
    def internal_error(error):
        """Handle 500 Internal Server Error."""
        db.session.rollback()  # Rollback any failed database transactions
        return render_template('500.html'), 500
